// $Id: cons_swap_equal.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <cstring>
#include <list>
#include <vector>
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <xpcom/array.hpp>
#include <xpcom/interface.hpp>
#include <xpcom/ptr_array.hpp>
#include <xpcom/string.hpp>
#include <xpcom/uuid.hpp>
#include <xpcom/detail/array_access.hpp>
#include "def.hpp"
#include "interface_def.hpp"

BOOST_AUTO_TEST_CASE_TEMPLATE(test_cons, T, test_types)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<T> Array;

    {
        Array a1;
        BOOST_CHECK(a1.empty());
        BOOST_CHECK_EQUAL(a1.size(), 0);
        BOOST_CHECK(array_access_impl(a1).data() == 0);

        Array a2(8);
        BOOST_CHECK(!a2.empty());
        BOOST_CHECK_EQUAL(a2.size(), 8);
        BOOST_CHECK(array_access_impl(a2).data() != 0);

        Array a3(0);
        BOOST_CHECK(a3.empty());
        BOOST_CHECK_EQUAL(a3.size(), 0);
        BOOST_CHECK(array_access_impl(a3).data() == 0);
    }
    BOOST_CHECK_EQUAL(A::count, 0);
}

BOOST_AUTO_TEST_CASE(test_cons_ptr)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef ptr_array<IA> Array;

    X_count_guard g;
    X_count(0);

    Array a1;
    BOOST_CHECK(a1.empty());
    BOOST_CHECK_EQUAL(a1.size(), 0);
    BOOST_CHECK(array_access_impl(a1).data() == 0);

    Array a2(8);
    BOOST_CHECK(!a2.empty());
    BOOST_CHECK_EQUAL(a2.size(), 8);
    BOOST_CHECK(array_access_impl(a2).data() != 0);

    Array a3(0);
    BOOST_CHECK(a3.empty());
    BOOST_CHECK_EQUAL(a3.size(), 0);
    BOOST_CHECK(array_access_impl(a3).data() == 0);

    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE(test_init_void_p)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<void*> Array;

    Array a1(8);
    for (Array::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK(a1[i] == 0);
    }
}

BOOST_AUTO_TEST_CASE(test_init_bool)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<bool> Array;

    Array a1(8);
    for (Array::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK(!a1[i]);
        BOOST_CHECK(a1[i].get() == false_);
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_init_pstring, T, test_types_pstring)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<T> Array;

    Array a1(8);
    BOOST_CHECK(a1.size() == 8);
    for (typename Array::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK(!a1[i]);
        BOOST_CHECK(a1[i].get() == 0);
    }

    Array a2;
    BOOST_CHECK(a2.empty());

    BOOST_CHECK(a1 != a2);

    a1.swap(a2);

    BOOST_CHECK(a2.size() == 8);
    for (typename Array::size_type i = 0; i < a2.size(); ++i) {
        BOOST_CHECK(!a2[i]);
        BOOST_CHECK(a2[i].get() == 0);
    }
    BOOST_CHECK(a1.empty());

    BOOST_CHECK(a1 != a2);
}

BOOST_AUTO_TEST_CASE(test_init_A)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<A> Array;

    Array a1(8);
    BOOST_CHECK(a1.size() == 8);
    for (Array::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK_EQUAL(a1[i].n, 9);
    }

    Array a2(4);
    BOOST_CHECK(a2.size() == 4);
    for (Array::size_type i = 0; i < a2.size(); ++i) {
        a2[i].n = i;
    }

    BOOST_CHECK(a1 != a2);

    a1.swap(a2);

    BOOST_CHECK(a2.size() == 8);
    for (Array::size_type i = 0; i < a2.size(); ++i) {
        BOOST_CHECK_EQUAL(a2[i].n, 9);
    }
    for (Array::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK(a1[i].n == static_cast<int>(i));
    }

    BOOST_CHECK(a1 != a2);
}

BOOST_AUTO_TEST_CASE(test_init_ptr)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef ptr_array<IB> Array;

    Array a1(8);
    for (Array::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK(!a1[i]);
        BOOST_CHECK(a1[i].get() == 0);
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_copy_integral, T, test_types_integral)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<T> Array;

    Array a1(8);
    for (typename Array::size_type i = 0; i < a1.size(); ++i) {
        a1[i] = static_cast<typename Array::value_type>(i);
    }

    Array a2(a1);
    BOOST_CHECK(a2.size() == 8);
    BOOST_CHECK(array_access_impl(a2).data() != array_access_impl(a1).data());
    BOOST_CHECK(a2 == a1);

    for (typename Array::size_type i = 0; i < a2.size(); ++i) {
        BOOST_CHECK_EQUAL(a2[i], static_cast<typename Array::value_type>(i));
    }

    BOOST_CHECK(a1 == a2);

    Array a3;
    a3.assign(a2);
    BOOST_CHECK(a3 == a1);
}

BOOST_AUTO_TEST_CASE(test_copy_bool)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<bool> Array;

    Array a1(8);
    for (Array::size_type i = 0; i < a1.size() - 2; ++i) {
        a1[i] = (i % 2 == 0);
    }

    Array a2(a1);
    BOOST_CHECK(a2.size() == 8);
    BOOST_CHECK(array_access_impl(a2).data() != array_access_impl(a1).data());
    BOOST_CHECK(a2 == a1);

    for (Array::size_type i = 0; i < a2.size() - 2; ++i) {
        if (i % 2)
            BOOST_CHECK(!a2[i]);
        else
            BOOST_CHECK(a2[i]);
    }

    BOOST_CHECK(!a2[6]);
    BOOST_CHECK(!a2[7]);

    BOOST_CHECK(a1 == a2);

    Array a3;
    a3.assign(a2);
    BOOST_CHECK(a3 == a1);
}

BOOST_AUTO_TEST_CASE(test_copy_pstring)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<char*> Array;

    Array a1(4);
    a1[0] = "A test1";
    a1[1] = "AA test2";
    //a1[2] = "AAA test3";
    a1[3] = "AAAA test4";
    BOOST_CHECK(std::strcmp(a1[0], "A test1") == 0);
    BOOST_CHECK(std::strcmp(a1[1], "AA test2") == 0);
    //BOOST_CHECK(std::strcmp(a1[2], "AAA test3") == 0);
    BOOST_CHECK(a1[2] == 0);
    BOOST_CHECK(std::strcmp(a1[3], "AAAA test4") == 0);

    Array a2(a1);
    BOOST_CHECK(a2.size() == 4);
    BOOST_CHECK(array_access_impl(a2).data() != array_access_impl(a1).data());
    BOOST_CHECK(a2 == a1);

    BOOST_CHECK(std::strcmp(a2[0], "A test1") == 0);
    BOOST_CHECK(std::strcmp(a2[1], "AA test2") == 0);
    //BOOST_CHECK(std::strcmp(a2[2], "AAA test3") == 0);
    BOOST_CHECK(a2[2] == 0);
    BOOST_CHECK(std::strcmp(a2[3], "AAAA test4") == 0);

    BOOST_CHECK(a1 == a2);

    Array a3;
    a3.assign(a2);
    BOOST_CHECK(a3 == a1);
}

BOOST_AUTO_TEST_CASE(test_copy_uuid)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<uuid> Array;

    uuid id1 = { 0xf489ad32, 0x769a, 0x4664,
            { 0xa7, 0xd4, 0x42, 0xbe, 0x1e, 0x19, 0xea, 0x66 } };
    uuid id3 = { 0 };

    Array a1(5);
    a1[0] = typeid_of<interface>();
    a1[1] = id1;
    //a1[2] = id2;
    a1[3] = id3;
    a1[4] = uuid::nil;
    uuid** data = array_access_impl(a1).data();
    BOOST_CHECK(data[0] != 0);
    BOOST_CHECK(data[1] != 0);
    BOOST_CHECK(data[2] == 0);
    BOOST_CHECK(data[3] != 0);
    BOOST_CHECK(data[4] != 0);

    Array a2(a1);
    BOOST_CHECK(a2.size() == 5);
    BOOST_CHECK(array_access_impl(a2).data() != array_access_impl(a1).data());
    BOOST_CHECK(a2 == a1);
    BOOST_CHECK(data[2] != 0);

    BOOST_CHECK(a2[0] == typeid_of<interface>());
    BOOST_CHECK(a2[1] == id1);
    BOOST_CHECK(a2[2].is_nil());
    BOOST_CHECK(a2[3].is_nil());
    BOOST_CHECK(a2[4].is_nil());

    BOOST_CHECK(a1 == a2);

    Array a3;
    a3.assign(a2);
    BOOST_CHECK(a3 == a1);
}

BOOST_AUTO_TEST_CASE(test_copy_A)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<A> Array;

    Array a1(5);
    for (Array::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK_EQUAL(a1[i].n, 9);
    }

    Array a2(a1);
    BOOST_CHECK(a2.size() == 5);
    BOOST_CHECK(array_access_impl(a2).data() != array_access_impl(a1).data());
    BOOST_CHECK(a2 == a1);
    for (Array::size_type i = 0; i < a2.size(); ++i) {
        BOOST_CHECK_EQUAL(a2[i].n, 9);
    }

    BOOST_CHECK(a1 == a2);

    Array a3;
    a3.assign(a2);
    BOOST_CHECK(a3 == a1);
}

BOOST_AUTO_TEST_CASE(test_copy_ptr)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef ptr_array<IBase> Array;

    X_count_guard g;
    X_count(0);
    {
        Array a1(5);
        for (Array::size_type i = 0; i < a1.size(); ++i) {
            a1[i].reset(X_make_instance());
            a1[i]->setN((int)i);
            BOOST_CHECK_EQUAL(a1[i]->getN(), (int)i);
            BOOST_CHECK_EQUAL(X_count(), (int)(i + 1));
            BOOST_CHECK_EQUAL(a1[i]->ref(), (refcnt_t)1);
        }
        BOOST_CHECK_EQUAL(X_count(), 5);

        Array a2(a1);
        BOOST_CHECK(a2.size() == 5);
        BOOST_CHECK(array_access_impl(a2).data() != array_access_impl(a1).data());
        BOOST_CHECK(a2 == a1);
        for (Array::size_type i = 0; i < a2.size(); ++i) {
            BOOST_CHECK_EQUAL(a2[i]->ref(), (refcnt_t)2);
            BOOST_CHECK_EQUAL(a2[i]->getN(), (int)i);
        }
        BOOST_CHECK_EQUAL(X_count(), 5);

        BOOST_CHECK(a1 == a2);

        Array a3;
        a3.assign(a2);
        BOOST_CHECK(a3 == a1);
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_range_integral, T, test_types_integral)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<T> Array;
    typedef std::vector<T> Vec;

    Vec a1(8);
    for (typename Vec::size_type i = 0; i < a1.size(); ++i) {
        a1[i] = static_cast<typename Array::value_type>(i);
    }

    Array a2(a1.begin(), a1.end());
    BOOST_CHECK(a2.size() == 8);

    for (typename Array::size_type i = 0; i < a2.size(); ++i) {
        BOOST_CHECK_EQUAL(a2[i], static_cast<typename Array::value_type>(i));
    }

    Array a3;
    a3.assign(a1.begin(), a1.end());
    BOOST_CHECK(a3 == a2);
}

BOOST_AUTO_TEST_CASE(test_range_bool)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<bool> Array;
    typedef std::vector<bool> Vec;

    Vec a1(8);
    for (Vec::size_type i = 0; i < a1.size() - 2; ++i) {
        a1[i] = (i % 2 == 0);
    }

    Array a2(a1.begin(), a1.end());
    BOOST_CHECK(a2.size() == 8);

    for (Array::size_type i = 0; i < a2.size() - 2; ++i) {
        if (i % 2)
            BOOST_CHECK(!a2[i]);
        else
            BOOST_CHECK(a2[i]);
    }

    BOOST_CHECK(!a2[6]);
    BOOST_CHECK(!a2[7]);

    Array a3;
    a3.assign(a1.begin(), a1.end());
    BOOST_CHECK(a3 == a2);
}

BOOST_AUTO_TEST_CASE(test_range_pstring)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<char*> Array;
    typedef std::list<string> List;

    List a1;
    a1.push_back("A test1");
    a1.push_back("AA test2");
    a1.push_back(string());
    a1.push_back("AAAA test4");

    Array a2(a1.begin(), a1.end());
    BOOST_CHECK(a2.size() == 4);

    BOOST_CHECK(std::strcmp(a2[0], "A test1") == 0);
    BOOST_CHECK(std::strcmp(a2[1], "AA test2") == 0);
    //BOOST_CHECK(std::strcmp(a2[2], "AAA test3") == 0);
    BOOST_CHECK(a2[2] == 0);
    BOOST_CHECK(std::strcmp(a2[3], "AAAA test4") == 0);

    Array a3;
    a3.assign(a1.begin(), a1.end());
    BOOST_CHECK(a3 == a2);
}

BOOST_AUTO_TEST_CASE(test_range_uuid)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<uuid> Array;

    uuid id1 = { 0xf489ad32, 0x769a, 0x4664,
            { 0xa7, 0xd4, 0x42, 0xbe, 0x1e, 0x19, 0xea, 0x66 } };
    uuid id3 = { 0 };

    Array a1(5);
    a1[0] = typeid_of<interface>();
    a1[1] = id1;
    //a1[2] = id2;
    a1[3] = id3;
    a1[4] = uuid::nil;
    uuid** data = array_access_impl(a1).data();
    BOOST_CHECK(data[0] != 0);
    BOOST_CHECK(data[1] != 0);
    BOOST_CHECK(data[2] == 0);
    BOOST_CHECK(data[3] != 0);
    BOOST_CHECK(data[4] != 0);

    Array a2(a1.begin(), a1.end());
    BOOST_CHECK(a2.size() == 5);
    BOOST_CHECK(array_access_impl(a2).data() != array_access_impl(a1).data());
    BOOST_CHECK(a2 == a1);
    BOOST_CHECK(data[2] != 0);

    BOOST_CHECK(a2[0] == typeid_of<interface>());
    BOOST_CHECK(a2[1] == id1);
    BOOST_CHECK(a2[2].is_nil());
    BOOST_CHECK(a2[3].is_nil());
    BOOST_CHECK(a2[4].is_nil());

    Array a3;
    a3.assign(a1.begin(), a1.end());
    BOOST_CHECK(a3 == a2);
}

BOOST_AUTO_TEST_CASE(test_range_A)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef array<A> Array;
    typedef std::vector<A> Vec;

    Vec a1(5);
    for (Vec::size_type i = 0; i < a1.size(); ++i) {
        BOOST_CHECK_EQUAL(a1[i].n, 9);
    }

    Array a2(a1.begin(), a1.end());
    BOOST_CHECK(a2.size() == 5);
    for (Array::size_type i = 0; i < a2.size(); ++i) {
        BOOST_CHECK_EQUAL(a2[i].n, 9);
    }

    Array a3;
    a3.assign(a1.begin(), a1.end());
    BOOST_CHECK(a3 == a2);
}

BOOST_AUTO_TEST_CASE(test_range_ptr)
{
    using namespace xpcom;
    using namespace xpcom::detail;
    typedef ptr_array<IBase> Array;
    typedef std::vector<ref_ptr<IA> > Vec;

    X_count_guard g;
    X_count(0);
    {
        Vec a1(5);
        for (Vec::size_type i = 0; i < a1.size(); ++i) {
            ref_ptr<IBase> p(X_make_instance());
            a1[i] = static_pointer_cast<IA>(p);
            a1[i]->setN((int)i);
            BOOST_CHECK_EQUAL(X_count(), (int)(i + 1));
            BOOST_CHECK_EQUAL(a1[i]->ref(), (refcnt_t)2);
        }
        BOOST_CHECK_EQUAL(X_count(), 5);

        Array a2(a1.begin(), a1.end());
        BOOST_CHECK(a2.size() == 5);
        for (Array::size_type i = 0; i < a2.size(); ++i) {
            BOOST_CHECK_EQUAL(a2[i]->ref(), (refcnt_t)2);
            BOOST_CHECK_EQUAL(a2[i]->getN(), (int)i);
        }

        BOOST_CHECK_EQUAL(X_count(), 5);

        Array a3;
        a3.assign(a1.begin(), a1.end());
        BOOST_CHECK(a3 == a2);
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}
